/**
 * created by 小卷毛, 2020/12/23
 * Copyright (c) 2020, 416143467@qq.com All Rights Reserved.
 * #                   *********                            #
 * #                  ************                          #
 * #                  *************                         #
 * #                 **  ***********                        #
 * #                ***  ****** *****                       #
 * #                *** *******   ****                      #
 * #               ***  ********** ****                     #
 * #              ****  *********** ****                    #
 * #            *****   ***********  *****                  #
 * #           ******   *** ********   *****                #
 * #           *****   ***   ********   ******              #
 * #          ******   ***  ***********   ******            #
 * #         ******   **** **************  ******           #
 * #        *******  ********************* *******          #
 * #        *******  ******************************         #
 * #       *******  ****** ***************** *******        #
 * #       *******  ****** ****** *********   ******        #
 * #       *******    **  ******   ******     ******        #
 * #       *******        ******    *****     *****         #
 * #        ******        *****     *****     ****          #
 * #         *****        ****      *****     ***           #
 * #          *****       ***        ***      *             #
 * #            **       ****        ****                   #
 */
package com.sgcc.pda.mdrh.task.safe.oss;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;

import com.alibaba.sdk.android.oss.ClientConfiguration;
import com.alibaba.sdk.android.oss.ClientException;
import com.alibaba.sdk.android.oss.OSSClient;
import com.alibaba.sdk.android.oss.ServiceException;
import com.alibaba.sdk.android.oss.callback.OSSProgressCallback;
import com.alibaba.sdk.android.oss.common.OSSLog;
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider;
import com.alibaba.sdk.android.oss.model.GetObjectRequest;
import com.alibaba.sdk.android.oss.model.GetObjectResult;
import com.alibaba.sdk.android.oss.model.PutObjectRequest;
import com.alibaba.sdk.android.oss.model.PutObjectResult;
import com.sgcc.pda.mdrh.task.safe.util.HttpUtil;
import com.sgcc.pda.sdk.utils.DateUtil;
import com.sgcc.pda.web.impl.HttpCallbackInterface;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

/**
 * 描 述：
 * 作 者：小卷毛  2020-12-23 14:58
 * 修改描述：
 * 修 改 人：
 * 修改版本：
 */
public class OSSHelper {

    private OSSClient mClient;
    private int mConnectionTimeout; //连接超时时间，默认15秒
    private int mSocketTimeout; //socket超时时间，默认15秒
    private int mMaxConcurrentRequest; //最大并发请求数，默认5个
    private int mMaxErrorRetry; //失败后最大重试次数，默认2次

    // 私有化构造函数
    private OSSHelper() {
        mConnectionTimeout = 15 * 1000;
        mSocketTimeout = 15 * 1000;
        mMaxConcurrentRequest = 5;
        mMaxErrorRetry = 2;
    }

    private static class OSSHelperHolder {
        static final OSSHelper INSTANCE = new OSSHelper();
    }

    /**
     * 获取单例对象
     */
    public static OSSHelper getInstance() {
        return OSSHelper.OSSHelperHolder.INSTANCE;
    }

    /**
     * 设置连接超时时间
     */
    public void setConnectionTimeout(int millseconds) {
        this.mConnectionTimeout = millseconds;
    }

    /**
     * 设置socket超时时间
     */
    public void setSocketTimeout(int millseconds) {
        this.mSocketTimeout = millseconds;
    }

    /**
     * 设置最大并发请求数
     */
    public void setMaxConcurrentRequest(int count) {
        this.mMaxConcurrentRequest = count;
    }

    /**
     * 设置失败后最大重试次数
     */
    public void setMaxErrorRetry(int count) {
        this.mMaxErrorRetry = count;
    }

    /**
     * OSS初始化，过期自动更新Token
     */
    @SuppressLint("CheckResult")
    public void initOSS(final Context context) {
        OSSLog.enableLog(); //开启日志
        Completable.timer(100, TimeUnit.MILLISECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .subscribe(new Action() {
                    @Override
                    public void run() {
                        // 配置选项，需要在子线程中创建对象
                        ClientConfiguration config = new ClientConfiguration();
                        config.setConnectionTimeout(mConnectionTimeout);
                        config.setSocketTimeout(mSocketTimeout);
                        config.setMaxConcurrentRequest(mMaxConcurrentRequest);
                        config.setMaxErrorRetry(mMaxErrorRetry);

                        mClient = new OSSClient(
                                context,
                                OSSConst.OSS_ENDPOINT,
                                new OSSAuthCredentialsProvider(context),
                                config
                        );
                    }
                });
    }

    /**
     * OSS初始化，手动更新Token
     */
    @SuppressLint("CheckResult")
    public void initOSSHand(
            final Context context,
            final OnCompleteListener listener) {
        Single.just("oss-token")
                .observeOn(Schedulers.io())
                .map(new Function<String, Boolean>() { //子线程
                    @Override
                    public Boolean apply(String s) {
                        // 同步鉴权oss
                        HttpUtil.getOSSTokenSync(
                                context,
                                new HttpCallbackInterface() {
                                    @Override
                                    public void httpSuccess(String result) {
                                        try {
                                            JSONObject obj = new JSONObject(result);
                                            if (obj.has("RT_F")
                                                    && "1".equals(obj.optString("RT_F"))) {
                                                JSONObject jsonObj = obj.optJSONObject("DTS");
                                                if (jsonObj != null
                                                        && jsonObj.has("accesskeyId")
                                                        && jsonObj.has("accesskeySecret")
                                                        && jsonObj.has("securityToken")
                                                ) {
                                                    OSSLog.enableLog(); //开启日志
                                                    OSSStsTokenCredentialProvider credentialProvider =
                                                            new OSSStsTokenCredentialProvider(
                                                                    jsonObj.optString("accesskeyId"),
                                                                    jsonObj.optString("accesskeySecret"),
                                                                    jsonObj.optString("securityToken")
                                                            );

                                                    //配置选项
                                                    ClientConfiguration config = new ClientConfiguration();
                                                    config.setConnectionTimeout(mConnectionTimeout);
                                                    config.setSocketTimeout(mSocketTimeout);
                                                    config.setMaxConcurrentRequest(mMaxConcurrentRequest);
                                                    config.setMaxErrorRetry(mMaxErrorRetry);

                                                    mClient = new OSSClient(
                                                            context,
                                                            OSSConst.OSS_ENDPOINT,
                                                            credentialProvider,
                                                            config);
                                                } else {
                                                    // 初始化失败
                                                    mClient = null;
                                                }
                                            } else {
                                                // 初始化失败
                                                mClient = null;
                                            }
                                        } catch (JSONException e) {
                                            e.printStackTrace();
                                            // 初始化失败
                                            mClient = null;
                                        }
                                    }

                                    @Override
                                    public void httpFailed(int errorCode, String errorMsg) {
                                        Log.e("auth-OSSToken:", errorCode + "：" + errorMsg);
                                        // 初始化失败
                                        mClient = null;
                                    }
                                }
                        );
                        // 判断是否初始化成功
                        return mClient != null;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean aBoolean) { //主线程回调
                        if (listener != null)
                            listener.onComplete(aBoolean);
                    }
                });
    }

    /**
     * OSS上传单个文件
     */
    @SuppressLint("CheckResult")
    public void uploadSingleImage(
            String filePath,
            final OnOSSStatusListener listener) {
        if (mClient == null) {
            Log.e("Upload-OSS:", "OSS未初始化！");

            if (listener != null)
                listener.OnError("OSS未初始化！", -1);
            return;
        }

        if (TextUtils.isEmpty(filePath)) {
            Log.e("Upload-OSS:", "上传文件路径为空！");

            if (listener != null)
                listener.OnError("上传文件路径为空！", -1);
            return;
        }

        Single.just(filePath)
                .map(new Function<String, PutObjectResult>() {
                    @Override
                    public PutObjectResult apply(String path) {
                        // 文件保存文件夹，根据日期分类
                        String timeFolder = DateUtil.getNowTime("yyyy-MM/dd/");

                        // 文件名
                        String fileType = path.substring(path.lastIndexOf("."));
                        String fileName = "file_" + System.currentTimeMillis() + fileType;

                        // 定义上传请求对象
                        PutObjectRequest mRequest =
                                new PutObjectRequest(
                                        OSSConst.BUCKET_NAME,
                                        OSSConst.OBJECK_KEY + timeFolder + fileName,
                                        path);

                        // 设置上传进度回调
                        mRequest.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
                            @Override
                            public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
                                if (listener != null)
                                    listener.onProgress(request, currentSize, totalSize, -1);
                            }
                        });

                        // 定义上传结果对象
                        PutObjectResult mResult = null;

                        try {
                            mResult = mClient.putObject(mRequest);
                            if (mResult != null) {
                                Map<String, String> header = mResult.getResponseHeader();
                                header.put(
                                        "url",
                                        OSSConst.OBJECK_KEY + timeFolder + fileName
                                );

                                mResult.setResponseHeader(header);
                            }
                        } catch (ClientException e) {
                            e.printStackTrace();
                            Log.e("Upload-OSS:", "本地上传异常！");

                            if (listener != null)
                                listener.OnError("本地上传异常！", -1);
                        } catch (ServiceException e) {
                            e.printStackTrace();
                            Log.e("Upload-OSS:", "上传服务异常！");

                            if (listener != null)
                                listener.OnError("上传服务异常！", -1);
                        }

                        return mResult;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(Disposable disposable) {
                        if (listener != null) listener.onBefore();
                    }
                })
                .doFinally(new Action() {
                    @Override
                    public void run() {
                        if (listener != null) listener.onAfter();
                    }
                })
                .subscribe(
                        new Consumer<PutObjectResult>() {
                            @Override
                            public void accept(PutObjectResult result) {
                                if (result != null) {
                                    if (listener != null)
                                        listener.onSuccess(result, -1);
                                } else {
                                    Log.e("Upload-OSS:", "文件上传失败！");

                                    if (listener != null)
                                        listener.OnError("文件上传失败！", -1);
                                }
                            }
                        },
                        new Consumer<Throwable>() {
                            @Override
                            public void accept(Throwable error) {
                                error.printStackTrace();
                                Log.e("Upload-OSS:", "文件上传异常！");

                                if (listener != null)
                                    listener.OnError("文件上传异常！", -1);
                            }
                        });
    }

    /**
     * OSS上传多个文件
     */
    @SuppressLint("CheckResult")
    public void uploadImages(
            final List<String> pathList,
            final OnOSSStatusListener listener) {
        if (mClient == null) {
            Log.e("Upload-OSS:", "OSS未初始化！");

            if (listener != null)
                listener.OnError("OSS未初始化！", -1);
            return;
        }

        if (pathList == null
                || pathList.isEmpty()) {
            Log.e("Upload-OSS:", "上传文件路径为空！");

            if (listener != null)
                listener.OnError("上传文件路径为空！", -1);
            return;
        }

        // 上传成功次数
        final int[] uploadCount = {0};

        Observable.fromIterable(pathList)
                .concatMap(new Function<String, ObservableSource<String>>() {
                    @Override
                    public ObservableSource<String> apply(String item) {
                        return Observable.just(item);
                    }
                })
                .map(new Function<String, PutObjectResult>() {
                    @Override
                    public PutObjectResult apply(final String path) {
                        // 文件保存文件夹，根据日期分类
                        String timeFolder = DateUtil.getNowTime("yyyy-MM/dd/");

                        // 文件名
                        String fileType = path.substring(path.lastIndexOf("."));
                        String fileName = "file_" + System.currentTimeMillis() + fileType;

                        // 定义上传请求对象
                        PutObjectRequest mRequest =
                                new PutObjectRequest(
                                        OSSConst.BUCKET_NAME,
                                        OSSConst.OBJECK_KEY + timeFolder + fileName,
                                        path);

                        // 设置上传进度回调
                        mRequest.setProgressCallback(new OSSProgressCallback<PutObjectRequest>() {
                            @Override
                            public void onProgress(PutObjectRequest request, long currentSize, long totalSize) {
                                if (listener != null)
                                    listener.onProgress(request, currentSize, totalSize, pathList.indexOf(path));
                            }
                        });

                        // 定义上传结果对象
                        PutObjectResult mResult = null;

                        try {
                            mResult = mClient.putObject(mRequest);

                            if (mResult != null) {
                                Map<String, String> header = mResult.getResponseHeader();
                                header.put(
                                        "url",
                                        OSSConst.OBJECK_KEY + timeFolder + fileName
                                );

                                mResult.setETag(String.valueOf(pathList.indexOf(path)));
                                mResult.setResponseHeader(header);
                            }
                        } catch (ClientException e) {
                            e.printStackTrace();
                            Log.e("Upload-OSS:", "本地上传异常！");

                            if (listener != null)
                                listener.OnError("本地上传异常！", pathList.indexOf(path));
                        } catch (ServiceException e) {
                            e.printStackTrace();
                            Log.e("Upload-OSS:", "上传服务异常！");

                            if (listener != null)
                                listener.OnError("上传服务异常！", pathList.indexOf(path));
                        }

                        return mResult;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(Disposable disposable) {
                        if (listener != null) listener.onBefore();
                    }
                })
                .doFinally(new Action() {
                    @Override
                    public void run() {
                        if (listener != null) listener.onAfter();
                    }
                })
                .subscribe(
                        new Consumer<PutObjectResult>() {
                            @Override
                            public void accept(PutObjectResult result) {
                                if (result != null) {
                                    uploadCount[0]++;

                                    if (listener != null)
                                        listener.onSuccess(result, Integer.parseInt(result.getETag()));
                                }
                            }
                        },
                        new Consumer<Throwable>() {
                            @Override
                            public void accept(Throwable error) {
                                error.printStackTrace();
                                Log.e("Upload-OSS:", "文件上传异常！");

                                if (listener != null)
                                    listener.OnError("文件上传异常！", -1);
                            }
                        },
                        new Action() {
                            @Override
                            public void run() {
                                if (listener != null)
                                    listener.onSuccess(null, uploadCount[0]);
                            }
                        }
                );
    }

    /**
     * OSS下载多个文件
     */
    @SuppressLint("CheckResult")
    public void downloadImages(
            final List<String> fileList,
            final OnOSSStatusListener listener) {
        if (mClient == null) {
            Log.e("Download-OSS:", "OSS未初始化！");

            if (listener != null)
                listener.OnError("OSS未初始化！", -1);
            return;
        }

        if (fileList == null
                || fileList.isEmpty()) {
            Log.e("Download-OSS:", "下载文件路径为空！");

            if (listener != null)
                listener.OnError("下载文件路径为空！", -1);
            return;
        }

        // 下载成功次数
        final int[] downloadCount = {0};

        Observable.fromIterable(fileList)
                .concatMap(new Function<String, ObservableSource<String>>() {
                    @Override
                    public ObservableSource<String> apply(String file) {
                        return Observable.just(file);
                    }
                })
                .map(new Function<String, GetObjectResult>() {
                    @Override
                    public GetObjectResult apply(final String url) {
                        // 定义下载请求对象
                        GetObjectRequest mRequest = new GetObjectRequest(
                                OSSConst.BUCKET_NAME,
                                url);

                        // 设置下载进度回调
                        mRequest.setProgressListener(new OSSProgressCallback<GetObjectRequest>() {
                            @Override
                            public void onProgress(GetObjectRequest request, long currentSize, long totalSize) {
                                if (listener != null)
                                    listener.onDownloadProgress(request, currentSize, totalSize, fileList.indexOf(url));
                            }
                        });

                        // 定义下载结果对象
                        GetObjectResult mResult = null;

                        try {
                            mResult = mClient.getObject(mRequest);

                            if (mResult != null) {
                                Map<String, String> header = mResult.getResponseHeader();

                                header.put("url", url);
                                header.put("tag", String.valueOf(fileList.indexOf(url)));

                                mResult.setResponseHeader(header);
                            }
                        } catch (ClientException e) {
                            e.printStackTrace();
                            Log.e("Download-OSS:", "本地上传异常！");

                            if (listener != null)
                                listener.OnError("本地上传异常！", fileList.indexOf(url));
                        } catch (ServiceException e) {
                            e.printStackTrace();
                            Log.e("Download-OSS:", "上传服务异常！");

                            if (listener != null)
                                listener.OnError("上传服务异常！", fileList.indexOf(url));
                        }

                        return mResult;
                    }
                })
                .map(new Function<GetObjectResult, GetObjectResult>() {
                    @Override
                    public GetObjectResult apply(GetObjectResult result) {
                        if (result != null) {
                            String path = result.getResponseHeader().get("url");

                            if (TextUtils.isEmpty(path)) return null;

                            // 本地保存目录
                            File appDir = new File(
                                    Environment.getExternalStorageDirectory(),
                                    "yxgkzy/image"
                            );
                            //创建新文件夹
                            if (!appDir.exists()) appDir.mkdirs();

                            // 文件名称
                            assert path != null;
                            String fileType = path.substring(path.lastIndexOf("."));
                            String fileName = System.currentTimeMillis() + fileType;

                            try {
                                File destFile = new File(appDir, fileName);
                                //创建新文件
                                if (destFile.exists()) destFile.createNewFile();

                                FileOutputStream output = new FileOutputStream(destFile);

                                byte[] buffer = new byte[1024];
                                int len;

                                while ((len = result.getObjectContent().read(buffer)) != -1) {
                                    // 处理下载的数据
                                    output.write(buffer, 0, len);
                                    output.flush();
                                }

                                output.close();
                                result.getObjectContent().close();

                                // 设置为本地路径
                                result.getResponseHeader()
                                        .put("url", destFile.getAbsolutePath());

                                return result;
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                        return null;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnSubscribe(new Consumer<Disposable>() {
                    @Override
                    public void accept(Disposable disposable) {
                        if (listener != null) listener.onBefore();
                    }
                })
                .doFinally(new Action() {
                    @Override
                    public void run() {
                        if (listener != null) listener.onAfter();
                    }
                })
                .subscribe(
                        new Consumer<GetObjectResult>() {
                            @Override
                            public void accept(GetObjectResult result) {
                                if (result != null) {
                                    downloadCount[0]++;

                                    if (listener != null) {
                                        String tagIndex = result.getResponseHeader().get("tag");

                                        if (tagIndex != null)
                                            listener.onDownloadSuccess(
                                                    result,
                                                    Integer.parseInt(tagIndex)
                                            );
                                    }
                                }
                            }
                        },
                        new Consumer<Throwable>() {
                            @Override
                            public void accept(Throwable error) {
                                error.printStackTrace();
                                Log.e("Download-OSS:", "文件下载异常！");

                                if (listener != null)
                                    listener.OnError("文件下载异常！", -1);
                            }
                        },
                        new Action() {
                            @Override
                            public void run() {
                                if (listener != null)
                                    listener.onSuccess(null, downloadCount[0]);
                            }
                        }
                );
    }

    /**
     * OSS签名URL
     */
    public String getRealUrl(String url) {
        if (url.contains("oss")) {
            try {
                return mClient.presignConstrainedObjectURL(
                        OSSConst.BUCKET_NAME,
                        url,
                        30 * 60
                );
            } catch (ClientException e) {
                e.printStackTrace();
            }
        }

        return url;
    }

    /**
     * 初始化完成回调接口
     */
    interface OnCompleteListener {
        /**
         * 完成回调
         *
         * @param isDone true成功 false失败
         */
        void onComplete(boolean isDone);
    }

}
